// Perspective
// (c) jimon game studio

#include "CPerspective_Console.h"

namespace je
{
	namespace app
	{
		#define JE_CONSOLE_COMMAND_CHAR ">"
		#define JE_CONSOLE_COMMAND_CHAR_CONST '>'
		#define JE_CONSOLE_TEXT_FONT "Test"
		#define JE_CONSOLE_TEXT_MATERIAL "TextNoLight"

		//! Logging Console
		typedef jeDynamicArray<CPerspective_Console*> WorkingConsolesType;
		WorkingConsolesType * WorkingConsoles = NULL;

		//! Console Log
		void ConsoleLog(const jeStringc & Log,core::ILogManager::LogMessageLevels WarningLevel)
		{
			if(WorkingConsoles)
				for(u32 i=0;i<WorkingConsoles->GetSize();i++)
					(*WorkingConsoles)[i]->PushTextToConsole(Log.c_str());
		}

		//! Register In Script
		u1 RegisterInScript = false;

		//! Clear Console Log
		void ClearConsoleLog()
		{
			if(WorkingConsoles)
				for(u32 i=0;i<WorkingConsoles->GetSize();i++)
					(*WorkingConsoles)[i]->ClearLog();
		}

		//! Constructor
		CPerspective_Console::CPerspective_Console(video::jeRenderWindow * RenderWindow)
			:CurrentRenderWindow(RenderWindow),ConsoleOpen(false),ConsoleSelect(-1)
		{
			CurrentRenderWindow->Grab();

			// Scene manager
			SceneManager = scene::CreateSceneManager(CurrentRenderWindow->GetRender(),scene::BSMC_DEFAULT);

			// Camera
			Camera = SceneManager->CreateCamera();
			Camera->SetCameraOptions(scene::ICameraSceneNode::CameraOptions().Set2D());
			SceneManager->SetCurrentCamera(Camera);

			// Layer
			//ConsoleBGLayer = SceneManager->CreateLayer();
			//ConsoleBGLayer->SetPriority(0);
			//ConsoleTextLayer = SceneManager->CreateLayer();
			//ConsoleTextLayer->SetPriority(1);

			// Fps text
			FpsText = CreateText("Fps : 0");

			// Base console pivot
			Console = SceneManager->CreatePivot();

			JENEW(ConsoleAnimator,jeConsoleAnimator)
			Console->AddAnimator(ConsoleAnimator);
			ConsoleAnimator->Drop();
			
			// Console text pivot
			ConsoleText = SceneManager->CreatePivot(Console);
			
			// Console background
			ConsoleBG = SceneManager->CreateSprite(Console);
			ConsoleBG->SetMaterialResource(JE_MATERIALRESOURCE("Console_BackGround"));

			// Console bg avatar
			ConsoleBGAvatar = SceneManager->CreateSprite(Console);
			ConsoleBGAvatar->SetMaterialResource(JE_MATERIALRESOURCE("Console_Avatar"));

			// Console cursor
			ConsoleCursor = SceneManager->CreatePivot(ConsoleText);
			scene::ITextSceneNode * ConsoleCursorText = CreateText("|",ConsoleCursor);
			ConsoleCursorText->SetPosition(jeVector3df(-2.0f,0,0)); // huh

			jeCursorAnimator * CursorAnimator = NULL;
			JENEW(CursorAnimator,jeCursorAnimator)
			ConsoleCursorText->AddAnimator(CursorAnimator);
			CursorAnimator->Drop();

			// Console input
			ConsoleInput = CreateText(JE_CONSOLE_COMMAND_CHAR,ConsoleText);

			// Set size optons
			WorkScreenSize = CurrentRenderWindow->GetWindow()->GetSize();

			// Resize
			OnResize();

			// Input controllers
			JENEW(ConsoleInputKeyConsole,input::jeInputController(input::KC_TILDA,CurrentRenderWindow->GetWindow()->GetKeyDriver()))
			JENEW(ConsoleInputKeyEnter,input::jeInputController(input::KC_RETURN,CurrentRenderWindow->GetWindow()->GetKeyDriver()))
			JENEW(ConsoleInputKeyUp,input::jeInputController(input::KC_UP,CurrentRenderWindow->GetWindow()->GetKeyDriver()))
			JENEW(ConsoleInputKeyDown,input::jeInputController(input::KC_DOWN,CurrentRenderWindow->GetWindow()->GetKeyDriver()))
			JENEW(ConsoleInputText,input::jeInputController(CurrentRenderWindow->GetWindow()->GetKeyDriver()))

			//! Logging Console
			if(!WorkingConsoles)
				JENEW(WorkingConsoles,WorkingConsolesType)
			WorkingConsoles->InsertElement(this);

			//! Set Logging Function
			core::GetLogManager()->SetLogCallback((core::ILogManager::jeLogFunction)&ConsoleLog);

			//! Clear Log
			if(!RegisterInScript)
			{
				JE_SCRIPT_REG_FUNCTION(ClearConsoleLog,"void Clear()")
				RegisterInScript = true;
			}
		}

		//! Destructor
		CPerspective_Console::~CPerspective_Console()
		{
			core::GetLogManager()->SetLogCallback(NULL);

			for(u32 i=0;i<WorkingConsoles->GetSize();i++)
				if((*WorkingConsoles)[i] == this)
				{
					WorkingConsoles->EraseElement(i);
					break;
				}

			if(WorkingConsoles->GetSize() == 0)
				JEDELETE(WorkingConsoles)

			ConsoleInputText->Drop();
			ConsoleInputKeyDown->Drop();
			ConsoleInputKeyUp->Drop();
			ConsoleInputKeyEnter->Drop();
			ConsoleInputKeyConsole->Drop();

			SceneManager->Drop();

			CurrentRenderWindow->Drop();
		}

		//! Tick
		void CPerspective_Console::Tick()
		{
		}

		//! On Update
		void CPerspective_Console::OnUpdate()
		{
			// update fps
			if(core::GetFpsCounter()->IsUpdated())
				FpsText->SetText(jeStringd("Fps : ")+jeStringd((s32)core::GetFpsCounter()->GetFps()));

			// if console opens or opened
			// Check resize
			jeVector2di ScreenSize = CurrentRenderWindow->GetWindow()->GetSize();
			if(WorkScreenSize != ScreenSize)
			{
				WorkScreenSize = ScreenSize;
				OnResize();
			}
			
			// Check console key
			if(ConsoleInputKeyConsole->IsHit())
			{
				if(ConsoleAnimator->GetState() == jeConsoleAnimator::CS_CLOSED)
				{
					ConsoleAnimator->SetState(jeConsoleAnimator::CS_OPEN);
					ConsoleOpen = true;
				}
				else
					ConsoleAnimator->SetState(jeConsoleAnimator::CS_CLOSE);

				ConsoleInputText->Flush();
			}

			// if console opened
			if(ConsoleAnimator->GetState() == jeConsoleAnimator::CS_OPENED)
			{
				// Set inputed text
				if(ConsoleOpen)
				{
					ConsoleInputText->Flush();
					ConsoleOpen = false;
				}
				else
					if(ConsoleInputText->CheckInputedText())
					{
						ConsoleInput->SetText(jeStringd(JE_CONSOLE_COMMAND_CHAR)+ConsoleInputText->GetText());
						ConsoleInput->SetPosition(jeVector3df(0,(f32)ConsoleInput->GetTextSize().Y,0));
						ConsoleCursor->SetPosition(jeVector3df((f32)ConsoleInput->GetFontResource()->GetFont()->CalculateTextSize(ConsoleInput->GetText().SubString(0,ConsoleInputText->GetCursorPos()+1)).X,(f32)ConsoleInput->GetTextSize().Y,0));
					}

				// Check input key
				if(ConsoleInputKeyEnter->IsHit() && (ConsoleInputText->GetText().GetSize()>0))
				{
					jeStringd Command = ConsoleInputText->GetText();
					ConsoleInputText->Flush();
					PushTextToConsole(jeStringd(JE_CONSOLE_COMMAND_CHAR)+Command);
					core::GetScriptManager()->ExecuteCommand(Command.c_str());
					ConsoleSelect = -1;
				}

				u1 NeedToSeekSelect = false;

				// Check input up key
				if(ConsoleInputKeyUp->IsDown(true))
				{
					ConsoleSelect++;
					NeedToSeekSelect = true;
				}

				// Check input down key
				if(ConsoleInputKeyDown->IsDown(true))
				{
					ConsoleSelect--;
					NeedToSeekSelect = true;
				}

				// If we need to select from older commands
				if(NeedToSeekSelect)
				{
					u1 TextSelected = false;
					jeStringd WorkText = JE_CONSOLE_COMMAND_CHAR;
	
					if(ConsoleSelect > -1)
					{
						jeStringd LastText;
						s32 i = 0;
						for(ConsoleTextListType::Iterator Iter = ConsoleTextList.begin();Iter != ConsoleTextList.end();Iter++)
						{
							if((*Iter)->GetText()[0] == JE_CONSOLE_COMMAND_CHAR_CONST)
							{
								LastText = (*Iter)->GetText();
								if(ConsoleSelect == i++)
								{
									TextSelected = true;
									break;
								}
							}
						}
						if(i > 0)
							WorkText = LastText;

						if(!TextSelected && (ConsoleSelect > (i-1)))
							ConsoleSelect = i-1;
					}
					else
						ConsoleSelect = -1;

					ConsoleInputText->SetText(WorkText.SubString(1,WorkText.GetSize()-1));
					ConsoleInputText->SetCursorPos(WorkText.GetSize()-1);
				}
			}

			//if(CurrentRenderWindow->GetWindow()->GetKeyDriver()->IsHit(input::KC_ESCAPE))
			//	SetRun(false);

			SceneManager->OnUpdate();
		}

		//! On Render
		void CPerspective_Console::OnRender()
		{
			//CurrentRenderWindow->GetRender()->ClearScreen();

			CurrentRenderWindow->GetRender()->SetDepthMode(video::DM_ALWAYS);
			SceneManager->OnRender();
			CurrentRenderWindow->GetRender()->SetDepthMode();
		}

		//! Set Run
		void CPerspective_Console::SetRun(u1 Mode)
		{
			Run = Mode;
		}

		//! Return true if run
		u1 CPerspective_Console::IsRun()
		{
			return Run;
		}
		
		//! Push Text To Console
		void CPerspective_Console::PushTextToConsole(const jeStringd & Text)
		{
			scene::ITextSceneNode * TextNode = CreateText(Text,ConsoleText);

			TextNode->SetVisible(Console->IsVisible());

			ConsoleTextList.push_front(TextNode);

			f32 Y = ConsoleInput->GetPosition().Y;
			for(ConsoleTextListType::Iterator Iter = ConsoleTextList.begin();Iter != ConsoleTextList.end();Iter++)
			{
				Y+=(f32)(*Iter)->GetTextSize().Y;
				(*Iter)->SetPosition(jeVector3df(0,Y,0));
			}
		}

		//! Clear Log
		void CPerspective_Console::ClearLog()
		{
			for(ConsoleTextListType::Iterator Iter = ConsoleTextList.begin();Iter != ConsoleTextList.end();Iter++)
				(*Iter)->RemoveItself();
			ConsoleTextList.clear();
		}

		//! On Resize
		void CPerspective_Console::OnResize()
		{
			jeVector2df ScreenSize = CurrentRenderWindow->GetWindow()->GetSize();

			// Set background size
			media::jeSharedMaterial * BGSharedMaterial = ConsoleBG->GetSharedMaterialResource();
			jeVector2df ImageSize = ScreenSize;
			if(BGSharedMaterial)
				ImageSize = BGSharedMaterial->GetTexture(0)->Get()[0]->GetSize();

			jeVector2df Koef = ScreenSize / ImageSize;
			Koef.Y = Koef.Y / 2.0f;
			
			ConsoleBG->SetScale(jeVector3df(Koef.X,Koef.Y,1));
			ConsoleBG->SetPosition(jeVector3df(0,ScreenSize.Y/4.0f,0));
			ConsoleAnimator->SetPositions(jeVector2df(0,ScreenSize.Y/2.0f));

			jeMatrix4f TexMat;
			TexMat.Scale(jeVector3df(Koef.X,Koef.Y,1));
			TexMat.Translate(jeVector3df(0,floor(Koef.Y)-Koef.Y,0));
			ConsoleBG->SetTextureMatrix(TexMat);

			// Set avatar position
			media::jeSharedMaterial * AvatarSharedMaterial = ConsoleBGAvatar->GetSharedMaterialResource();
			jeVector2df AvatarImageSize;
			if(AvatarSharedMaterial)
				AvatarImageSize = AvatarSharedMaterial->GetTexture(0)->Get()[0]->GetSize();
			ConsoleBGAvatar->SetPosition(jeVector3df(ScreenSize.X/2.0f-AvatarImageSize.X/2.0f,AvatarImageSize.Y/2.0f,0));

			// Set text position
			ConsoleText->SetPosition(jeVector3df(-ScreenSize.X/2.0f,0,0));

			// Set console input pos
			ConsoleInput->SetPosition(jeVector3df(0,(f32)ConsoleInput->GetTextSize().Y,0));

			// Set fps text position
			FpsText->SetPosition(jeVector3df(-ScreenSize.X/2.0f+2.0f,ScreenSize.Y/2.0f,0));

			// Init state
			if(ConsoleAnimator->GetState() == jeConsoleAnimator::CS_NOTHING)
			{
				ConsoleAnimator->SetState(jeConsoleAnimator::CS_CLOSED);
				Console->SetPosition(jeVector3df(0,ScreenSize.Y/2.0f,0.0f));
				Console->SetVisible(false);
			}
		}

		//! Create Text
		scene::ITextSceneNode * CPerspective_Console::CreateText(const jeStringd & Text,scene::ISceneNode * Parent)
		{
			scene::ITextSceneNode * TextNode = SceneManager->CreateText(Text,Parent);
			
			TextNode->SetFontResource(JE_FONTRESOURCE(JE_CONSOLE_TEXT_FONT));
			TextNode->SetMaterialResource(JE_MATERIALRESOURCE(JE_CONSOLE_TEXT_MATERIAL));

			scene::ITextSceneNode::TextOptions TxtOpt;
			TxtOpt.DynamicMode = true;
			TxtOpt.OriginInCenter = false;
			TextNode->SetTextOptions(TxtOpt);

			return TextNode;
		}

		//! Create Console
		IPerspective_Console * CreateConsolePerspective(video::jeRenderWindow * RenderWindow)
		{
			IPerspective_Console * Console = NULL;
			JENEW(Console,CPerspective_Console(RenderWindow))
			return Console;
		}
	}
}
